﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text; 
using MyBasicLib.Extensions;
using MyBasicLib.Linq.Expressions;
using MyBasicLibLinqExpressions = MyBasicLib.Linq.Expressions;
using MyBasicLib.Data.ORMLiteV2;

namespace MyBasicLib.Data.ORMLiteNew.SqlClient
{
    public class ExpressionTranslator : System.Linq.Expressions.ExpressionVisitor
    {
        private HashSet<Expression> _candidates;

        private int _parameterCount;

        public ExpressionTranslator()
        {
            ParameterValues = new List<object>();
        }

       // protected IMappingProvider MappingProvider { get; private set; }
        protected StringBuilder Sb { get; private set; }
        protected TableNameAliasCache TableAliasCache { get; private set; }

        protected virtual bool NeedNominate
        {
            get { return true; }
        }

        public List<object> ParameterValues { get; private set; }

        public virtual void Translate(Expression exp, StringBuilder sb, int parameterCount,
                                      TableNameAliasCache tableAliasCache, IMappingProvider mappingProvider)
        {
         //   MappingProvider = mappingProvider;
            Sb = sb;
            _parameterCount = parameterCount;
            TableAliasCache = tableAliasCache;
            if (exp == null)
                return;
            if (NeedNominate)
                _candidates = new Nominator().Nominate(exp);
            Visit(exp);
        }

        public virtual void HandleParameter(StringBuilder sb, object par)
        {
            sb.Append("@p");
            sb.Append(_parameterCount++);
            ParameterValues.Add(par);
        }

        public override Expression Visit(Expression exp)
        {
            if (_candidates != null && _candidates.Contains(exp))
            {
                HandleParameter(Sb, exp.Eval());
                return exp;
            }
            return base.Visit(exp);
        }

        protected override Expression VisitConstant(ConstantExpression c)
        {
            HandleParameter(Sb, c.Value);
            return c;
        }

        protected override Expression VisitMember(MemberExpression m)
        {
            string ali = TableAliasCache.GetTableAlias(m);

            Sb.AppendFormat("[{0}].[{1}]", ali, m.Member.Name);

            return m;
        }


        protected override Expression VisitBinary(BinaryExpression b)
        {
            var shouldAddBracket = ShouldAddBracket(b, b.Left);
            VisitOperand(b.Left, shouldAddBracket);

            AppendNodeType(b, Sb);

            shouldAddBracket = ShouldAddBracket(b, b.Right);
            VisitOperand(b.Right, shouldAddBracket);
            return b;
        }

        protected void VisitOperand(Expression b, bool shouldAddBracket)
        {
            if (shouldAddBracket) Sb.Append("(");
            Visit(b);
            if (shouldAddBracket) Sb.Append(")");
        }

        protected static bool ShouldAddBracket(Expression fullExp, Expression exp)
        {
            return GetPrecedence(fullExp) < GetPrecedence(exp);
        }
        /// <summary>
        /// 获取操作符的优先级,数字越小优先级越高
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        /// <remarks>
        /// <![CDATA[
        /// 1   ~ (Bitwise NOT)
        /// 2   * (Multiply), / (Division), % (Modulo)
        /// 3   + (Positive), - (Negative), + (Add), (+ Concatenate), - (Subtract), & (Bitwise AND), ^ (Bitwise Exclusive OR), | (Bitwise OR)
        /// 4   =, >, <, >=, <=, <>, !=, !>, !< (Comparison operators)
        /// 5   NOT
        /// 6   AND
        /// 7   ALL, ANY, BETWEEN, IN, LIKE, OR, SOME
        /// 8   = (Assignment) 
        /// ]]> 
        /// </remarks>
        protected static int GetPrecedence(Expression expression)
        {

            var expressionType = expression.NodeType;

            switch (expressionType)
            {
                case ExpressionType.OrElse:
                    return 7;
                case ExpressionType.AndAlso:
                    return 6;
                case ExpressionType.Not:
                    return expression.Type == typeof(bool) ? 5 : 1;
                case ExpressionType.GreaterThan:
                case ExpressionType.GreaterThanOrEqual:
                case ExpressionType.LessThan:
                case ExpressionType.LessThanOrEqual:
                case ExpressionType.Equal:
                case ExpressionType.NotEqual:
                    return 4;
                case ExpressionType.Add:
                case ExpressionType.AddChecked:
                case ExpressionType.Subtract:
                case ExpressionType.SubtractChecked:
                case ExpressionType.ExclusiveOr:
                    return 3;
                case ExpressionType.And:
                    return expression.Type == typeof(bool) ? 6 : 3;
                case ExpressionType.Or:
                    return expression.Type == typeof(bool) ? 7 : 3;
                case ExpressionType.Multiply:
                case ExpressionType.MultiplyChecked:
                case ExpressionType.Divide:
                case ExpressionType.Modulo:
                    return 2;
            }
            return 0;
        }
        protected static void AppendNodeType(Expression expression, StringBuilder sb)
        {
            var expressionType = expression.NodeType;
            switch (expressionType)
            {
                case ExpressionType.And:
                case ExpressionType.AndAlso:
                    sb.Append(expression.Type == typeof(bool) ? " AND " : " & ");
                    break;
                case ExpressionType.Or:
                case ExpressionType.OrElse:
                    sb.Append(expression.Type == typeof(bool) ? " OR " : " | ");
                    break;
                case ExpressionType.ExclusiveOr:
                    sb.Append(" ^ ");
                    break;
                case ExpressionType.GreaterThan:
                    sb.Append(" > ");
                    break;
                case ExpressionType.GreaterThanOrEqual:
                    sb.Append(" >= ");
                    break;
                case ExpressionType.LessThan:
                    sb.Append(" < ");
                    break;
                case ExpressionType.LessThanOrEqual:
                    sb.Append(" <= ");
                    break;
                case ExpressionType.Equal:
                    sb.Append(" = ");
                    break;
                case ExpressionType.NotEqual:
                    sb.Append(" <> ");
                    break;
                case ExpressionType.Add:
                case ExpressionType.AddChecked:
                    sb.Append(" + ");
                    break;
                case ExpressionType.Subtract:
                case ExpressionType.SubtractChecked:
                    sb.Append(" - ");
                    break;
                case ExpressionType.Multiply:
                case ExpressionType.MultiplyChecked:
                    sb.Append(" * ");
                    break;
                case ExpressionType.Divide:
                    sb.Append(" / ");
                    break;
                case ExpressionType.Modulo:
                    sb.Append(" % ");
                    break;
                case ExpressionType.Not:
                    sb.Append(expression.Type == typeof(bool) ? " NOT " : " ~ ");
                    break;
            }
        }

        protected string GetExpressionString(Action act)
        {
            int idx = Sb.Length;
            act();
            string str = Sb.ToString(idx, Sb.Length - idx);
            Sb.Length = idx;
            return str;
        }
    }
}